package com.zc.config;

import com.zc.exception.AccessDeniedHandlerImpl;
import com.zc.exception.AuthenticationEntryPointImpl;
import com.zc.filter.JwtAuthenticationTokenFilter;
import com.zc.pojo.User;
import com.zc.service.impl.UserDetailServiceImpl;
import com.zc.service.impl.UserServiceImpl;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.ProviderManager;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.config.annotation.authentication.configuration.EnableGlobalAuthentication;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.config.annotation.web.configurers.AnonymousConfigurer;
import org.springframework.security.config.annotation.web.configurers.CorsConfigurer;
import org.springframework.security.config.annotation.web.configurers.FormLoginConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.context.HttpSessionSecurityContextRepository;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;

import java.security.Provider;
import java.util.Arrays;

/**
 * @description:
 * @author: cong
 * @create: 2024/8/2
 */
@Configuration
@EnableWebSecurity
//启用基于Spring Security的认证机制，允许对所有使用了@Secured或@PreAuthorize等注解的方法进行安全性控制。
@EnableGlobalAuthentication
//开启方法级安全控制，支持@PreAuthorize和@PostAuthorize等注解用于在方法执行前或后进行权限检查。
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig {
    @Autowired
    private AuthenticationConfiguration authenticationConfiguration;

    @Autowired
    JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter;

    @Autowired
    AuthenticationEntryPointImpl authenticationEntryPoint;

    @Autowired
    AccessDeniedHandlerImpl accessDeniedHandler;

    /**
     * 配置密码编码器
     *
     * @return 返回一个BCrypt密码编码器实例，用于加密和验证用户密码
     */
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }


    /**
     * 配置Security过滤器链
     * 该方法用于定制Spring Security的安全过滤器链，以实现JWT认证和授权机制
     *
     * @param http HttpSecurity对象，用于配置与HTTP请求相关的安全策略
     * @return 返回配置好的SecurityFilterChain对象
     * @throws Exception 如果配置过程中发生错误，可能会抛出异常
     */
    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        return http.csrf(AbstractHttpConfigurer::disable) // 禁用跨站请求伪造（CSRF）保护，因为JWT令牌不需要这种保护
                // spring security异常处理s
                .exceptionHandling(exception -> exception.authenticationEntryPoint(authenticationEntryPoint)
                        .accessDeniedHandler(accessDeniedHandler))
                // 配置无状态会话管理，表示服务器不存储会话信息，与JWT认证方式一致
                .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
//                .formLogin(FormLoginConfigurer::disable)
//                .logout(logout -> logout.logoutUrl("/exit").permitAll())
                // 所有其他请求都需要经过身份验证,允许所有用户访问以"/login"开头的请求，便于公开访问用户相关接口
                .authorizeHttpRequests(auth -> auth.requestMatchers(HttpMethod.POST, "/login").permitAll()
                        .anyRequest().authenticated())
                // 配置匿名访问规则，指定特定路径下允许匿名访问
                .anonymous(configurer -> configurer.authorities("/login", "/hello"))
                //添加一个自定义的过滤器 jwtAuthenticationTokenFilter，并且将其插入到UsernamePasswordAuthenticationFilter 过滤器之前。
                .addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class)
                // 配置跨域 CORS必须在Spring Security之前处理，因为预检请求将不包含任何cookie（即 JSESSIONID）。如果请求不包含任何cookie，
                // 而Spring Security在先，那么请求将确定用户没有得到认证（因为请求中没有cookie），并拒绝它。
                .cors(cors -> cors.configurationSource(corsConfigurationSource()))
                // 配置了一个HttpSessionSecurityContextRepository实例作为SecurityContext的存储库。
                // 它使得安全性上下文信息能够存储在用户的HTTP会话中，确保跨请求的数据一致性 todo:登录l
                .securityContext(securityContext -> securityContext.securityContextRepository(new HttpSessionSecurityContextRepository()))
                .build();

    }

    @Autowired
    UserDetailServiceImpl userDetailService;
    @Bean
    public AuthenticationManager authenticationManager() throws Exception {
        DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
        provider.setUserDetailsService(userDetailService);
        provider.setPasswordEncoder(passwordEncoder());
        return new ProviderManager(provider);
    }

    /**
     * 配置CORS，解决跨域问题
     * 通过@Bean注解声明一个CorsConfigurationSource Bean，该Bean定义了允许的源和方法，
     * 以确保Web应用可以跨域请求
     *
     * @return 配置好的CorsConfigurationSource实例
     */
    @Bean
    CorsConfigurationSource corsConfigurationSource() {
        // 创建CORS配置实例
        CorsConfiguration configuration = new CorsConfiguration();
        // 设置允许的来源，仅允许来自example.com的请求
        configuration.setAllowedOrigins(Arrays.asList("https://example.com"));
        // 设置允许的HTTP方法，包括GET和POST
        configuration.setAllowedMethods(Arrays.asList("GET","POST"));
        // 创建基于URL的CORS配置源
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        // 注册全局CORS配置，应用于所有请求路径
        source.registerCorsConfiguration("/**", configuration);
        // 返回配置好的CORS配置源
        return source;
    }
}
